/**
 * 
 */
package pvt.delez.pa.maven;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.operation.IRunnableWithProgress;

import pam.CombinationLibrary;
import pam.JarLibrary;
import pam.Library;
import pam.PamFactory;
import pam.Project;
import pam.ProjectType;
import pam.VariableLibrary;

/**
 * 生成项目依赖关系， 生成pom基本配置 包括parent.pom, 一个基本的pom; 基本的pom包含公有的library. 及下面子模块的pom，
 * 模块之间的依赖关系． 对于依赖的library, 可能不能从maven库准确的定位（坐标).
 * 
 * @author Administrator
 * 
 */
public class GenPom implements IRunnableWithProgress {
	private IProgressMonitor monitor;
	private VelocityEngine ve;
	private Map<String, Library> librarys = new HashMap<String, Library>();

	public GenPom() {
		super();
		this.ve = new VelocityEngine();
		// ve.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM, this);
		Properties props = new Properties();
		InputStream is = GenPom.class.getResourceAsStream("ve.properties");
		if (is == null) {
			is = Thread.currentThread().getContextClassLoader()
					.getResourceAsStream("/pvt/delez/pa/maven/ve.properties");
		}
		try {
			props.load(is);
			Thread.currentThread().setContextClassLoader(GenPom.class.getClassLoader());
			ve.init(props);
			System.out.println(ve.getProperty(VelocityEngine.RESOURCE_LOADER));
		} catch (Exception e) {
			e.printStackTrace();
			ve.setProperty(VelocityEngine.RESOURCE_LOADER, "class");
		}
	}

	@Override
	public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
		this.monitor = monitor;
		analysis();
		this.monitor = null;
	}

	public Object execute(ExecutionEvent event) throws ExecutionException {
		this.monitor = new NullProgressMonitor();
		analysis();
		this.monitor = null;
		return null;

	}

	private void analysis() {
		// Get the root of the workspace
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot root = workspace.getRoot();
		// Get all projects in the workspace
		IProject[] sysProjects = root.getProjects();
		Project[] myProjects = new Project[sysProjects.length];
		monitor.beginTask("生成整个工程POM", 2 * sysProjects.length + 1);

		int step = 0;
		// Loop over all projects
		for (IProject project : sysProjects) {
			monitor.setTaskName("分析项目-" + project.getName());
			myProjects[step++] = analysis(project);
			monitor.worked(step);
		}
		// 分析多项目主文件
		VelocityContext projectCtx = new VelocityContext();
		projectCtx.put("modules", myProjects);

		outPOM("project.vm", root.getLocation().toFile(), projectCtx);

		monitor.setTaskName("分析多项目主文件");
		monitor.worked(step++);

		for (Project project : myProjects) {
			monitor.setTaskName("生成项目-" + project.getName() + "的POM文件");
			VelocityContext moduleCtx = new VelocityContext(projectCtx);
			moduleCtx.put("module", project);
			switch (project.getType()) {
			case DYNAMIC_WEB_PROJECT:
				outPOM("web_module.vm", new File(project.getPath()), moduleCtx);
				break;

			default:
				outPOM("module.vm", new File(project.getPath()), moduleCtx);
				break;
			}

			monitor.worked(step);
		}
		//
		monitor.done();
	}

	private void outPOM(String vm, File location, VelocityContext ctx) {
		System.out.println(location);
		Template projectTemp = ve.getTemplate("/pvt/delez/pa/maven/" + vm, "UTF-8");

		File pomFile = new File(location, "pom.xml");
		FileWriter pomWriter = null;
		try {
			pomWriter = new FileWriter(pomFile);
			projectTemp.merge(ctx, pomWriter);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (pomWriter != null) {
				try {
					pomWriter.close();
				} catch (IOException e) {
					// ignore
				}
			}
		}
	}

	private Project createProject(String projectName) {

		Project project = PamFactory.eINSTANCE.createProject();
		project.setName(projectName);
		return project;
	}

	private Project analysis(IProject depSysProject) {
		IJavaProject depJavaProject = JavaCore.create(depSysProject);
		if (depJavaProject == null) {
			try {
				depJavaProject = (IJavaProject) depSysProject.getNature(JavaCore.NATURE_ID);
			} catch (CoreException e) {
				e.printStackTrace();
			}
		}
		Project p;
		if (depJavaProject == null) {
			p = createProject(depSysProject.getName());
			p.setType(ProjectType.GERENAL);
		} else {
			p = analysis(depJavaProject);
			try {
				if (depSysProject.hasNature("org.eclipse.wst.common.project.facet.core.nature")) {
					p.setType(ProjectType.DYNAMIC_WEB_PROJECT);
				} else {
					p.setType(ProjectType.JAVA);
				}
			} catch (CoreException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		p.setPath(depSysProject.getLocation().toFile().toString());
		return p;
	}

	public Project analysis(IJavaProject sysProject) {
		String projectName = sysProject.getProject().getName();

		Project project = createProject(projectName);
		build(sysProject, project);
		return project;
	}

	private void build(IJavaProject sysProject, Project project) {
		try {
			IClasspathEntry[] entrys = sysProject.readRawClasspath();
			for (IClasspathEntry e : entrys) {
				String key = e.getPath().toString();
				if (e.getEntryKind() != IClasspathEntry.CPE_PROJECT) {
					if (librarys.containsKey(key)) {
						Library lib = librarys.get(key);
						project.getLibraries().add(lib);
						lib.incRef();
						continue;
					}
				}
				monitor.subTask("分析依赖－" + key);
				switch (e.getEntryKind()) {
				case IClasspathEntry.CPE_PROJECT:
					String depName = e.getPath().toFile().getName();
					IProject depSysProject = ResourcesPlugin.getWorkspace().getRoot().getProject(depName);
					Project depProject = createProject(depSysProject.getName());
					if (depProject != null) {
						project.getDepends().add(depProject);
					}
					break;
				case IClasspathEntry.CPE_CONTAINER:

					CombinationLibrary cl = PamFactory.eINSTANCE.createCombinationLibrary();

					cl.setPath(e.getPath().toString());

					IClasspathContainer container = JavaCore.getClasspathContainer(e.getPath(), sysProject);
					cl.setName(container == null ? e.getPath().lastSegment() : container.getPath().lastSegment());
					if (container != null) {
						IClasspathEntry[] subEntrys = container.getClasspathEntries();
						for (IClasspathEntry sub : subEntrys) {
							cl.getPaths().add(sub.getPath().toString());
						}
					}
					project.getLibraries().add(cl);
					librarys.put(key, cl);
					break;

				case IClasspathEntry.CPE_LIBRARY:
					JarLibrary lib = PamFactory.eINSTANCE.createJarLibrary();

					IPath path = e.getPath();
					String ext = path.getFileExtension();
					String lastSegment = path.lastSegment();
					lib.setName(lastSegment);
					lib.setPath(path.toString());

					project.getLibraries().add(lib);
					librarys.put(key, lib);
					IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(e.getPath());

					ZipFileStructure zfs = new ZipFileStructure(new ZipFile(file.getLocation().toFile()));
					ZipEntry entry = zfs.getEntry("META-INF/maven/.+/pom.properties");
					if (entry != null) {
						InputStream is = zfs.getContents(entry);
						Properties props = new Properties();
						props.load(is);
						// version=3.2.1
						// groupId=commons-collections
						// artifactId=commons-collections
						lib.setGroupId(props.getProperty("groupId"));
						lib.setArtifactId(props.getProperty("artifactId"));
						lib.setVersion(props.getProperty("version"));
						is.close();
					} else {
						boolean crawlered = false;
						try {
							URL url = new URL("https://mvnrepository.com/search?q=" + lastSegment);
							URLConnection conn = url.openConnection();
							String body = getBody(conn);
							if (body != null) {
								String name = lastSegment.substring(0, lastSegment.length() - ext.length() -1);
								url = new URL("https://mvnrepository.com/search?q=" + name);
								conn = url.openConnection();
								body = getBody(conn);
							}
							if (body != null) {
								String prev = "<a href=\"/artifact/";
								int index = body.indexOf(prev);
								if (index > 0) {
									int end = body.indexOf("\">", index);
									String href = body.substring(index + prev.length(), end);
									crawlered = buildFromList(href, lib);
								}
							}else{
								System.out.println("lost from mvnrepository: " + url);
							}
							
						} catch (Exception e2) {
							e2.printStackTrace();
						}
						if (!crawlered) {
							// commons-cli-1.2.jar
							String name = lastSegment.substring(0, lastSegment.length() - ext.length() -1);
							String version = "-1";
							int index = name.lastIndexOf("-");
							if (index > 0) {
								version = name.substring(index + 1);
								name = name.substring(0, index);
							}
							lib.setGroupId(name);
							lib.setArtifactId(name);
							lib.setVersion(version);
						}
					}
					break;

				case IClasspathEntry.CPE_SOURCE:
					break;
				case IClasspathEntry.CPE_VARIABLE:
					IPath path2 = e.getPath();
					String varName = path2.segment(0);

					VariableLibrary var = PamFactory.eINSTANCE.createVariableLibrary();
					var.setName(path2.lastSegment());
					var.setExtensionPath(path2.toString());
					IPath p = JavaCore.getClasspathVariable(varName);
					var.setPath(p == null ? "?" : p.toString());

					project.getLibraries().add(var);
					librarys.put(key, var);
					break;
				default:
					break;
				}
			}
			System.out.println("librarys:" + librarys);

		} catch (Exception e) {
			System.err.println("分析失败：" + sysProject);
			e.printStackTrace();
		}

	}

	private boolean buildFromList(String href, Library lib) throws IOException {
		URL url = new URL("https://mvnrepository.com/artifact/" + href);
		URLConnection conn = url.openConnection();
		String body = getBody(conn);;
		int index = body.indexOf("<th>New Artifact</th>");
		if (index > 0) {
			// <a
			// href="/artifact/org.apache.commons/commons-lang3">commons-lang3</a>
			index = body.indexOf("<a href=\"/artifact/");
			if (index > 0) {
				int end = body.indexOf("\">", index);
				String newhref = body.substring(index + "<a href=\"".length(), end);
				if(newhref.charAt(0) != '/'){
					newhref = href.substring(0, href.lastIndexOf('/') + 1)+ newhref;
				}
				if (!newhref.equals(href)) {
					return buildFromList(newhref, lib);
				}
			}
		}
		index = body.indexOf("\" class=\"vbtn ");
		if (index > 0) {
			// <a href="commons-lang/2.6" class="vbtn release">2.6</a>
			// <a href="commons-lang/1.0-b1.1" class="vbtn beta">1.0-b1.1</a>
			int start = body.lastIndexOf("<a href=\"", index);
			String verHref = body.substring(start + "<a href=\"".length(), index);
			if(verHref.charAt(0) != '/'){
				verHref = href.substring(0, href.lastIndexOf('/')+ 1) + verHref;
			}

			return build(verHref, lib);
		} else {
			return false;
		}

	}

	private boolean build(String verHref, Library lib) throws IOException {
		System.out.println(verHref);
		URL url = new URL("https://mvnrepository.com/artifact/" + verHref);
		URLConnection conn = url.openConnection();
		String body = getBody(conn);;
		int index = body.indexOf("<textarea id=\"maven-");
		// <textarea id="maven-a" readonly="true" rows="7" cols="40"
		// style="width: 100%" class="snippet">&lt;!--
		// https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
		// --&gt;
		// &lt;dependency&gt;
		// &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
		// &lt;artifactId&gt;commons-lang3&lt;/artifactId&gt;
		// &lt;version&gt;3.4&lt;/version&gt;
		// &lt;/dependency&gt;
		// </textarea>
		if (index > 0) {
			lib.setGroupId(getText(body, "groupId", index));
			lib.setArtifactId(getText(body, "artifactId", index));
			lib.setVersion(getText(body, "version", index));

			return true;
		}
		return false;
	}

	private String getText(String body, String key, int index) {
		String tag = "&lt;" + key + "&gt;";
		String endTag = "&lt;/" + key + "&gt;";
		int start = body.indexOf(tag, index) + tag.length();
		int end = body.indexOf(endTag);
		return body.substring(start, end);
	}

	public static String getBody(URLConnection conn) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
		String brLine;
		StringBuilder buffer = new StringBuilder();
		while ((brLine = br.readLine()) != null) {
			buffer.append(brLine);
			buffer.append('\n');
		}
		br.close();
		return buffer.toString();

	}

	public static void main(String[] args) throws IOException {
		URL url = new URL("https://mvnrepository.com/search?q=" + "htmllexer.jar");
		URLConnection conn = url.openConnection();
		String body = getBody(conn);
		System.out.println(body);
	}

}
